package cn.maihe.elg.operation.centers.hymake.channel.service;

import cn.hutool.core.codec.Base64;
import cn.maihe.elg.operation.centers.hymake.auth.HyMakeCenterAuthConfig;
import cn.maihe.elg.operation.centers.hymake.channel.HyMakeBaseChannelService;
import cn.maihe.elg.operation.centers.hymake.config.HyMakeInfoConfig;
import cn.maihe.elg.operation.centers.hymake.dto.channel.BondDownLoadReqDTO;
import cn.maihe.elg.operation.centers.hymake.dto.channel.ChlBizRespData;
import cn.maihe.elg.operation.centers.hymake.enums.HyMakeChannelMethod;
import cn.maihe.elg.operation.centers.hymake.enums.XMChannelMethod;
import cn.maihe.elg.operation.centers.supports.model.bo.PushCenterGuaranteeBo;
import cn.maihe.elg.operation.model.dto.InnerBaseResp;
import cn.maihe.elg.operation.model.enums.AttachmentFileType;
import cn.maihe.elg.operation.model.enums.CenterNoEnum;
import cn.maihe.elg.operation.model.enums.InnerRespCode;
import cn.maihe.elg.operation.repository.entity.AcceptOrderInfoDO;
import cn.maihe.elg.operation.repository.entity.GuaranteeAttachmentInfoDO;
import cn.maihe.elg.operation.repository.entity.GuaranteeVo;
import cn.maihe.elg.operation.service.business.BidCustomerService;
import cn.maihe.elg.operation.service.order.AcceptOrderInfoService;
import cn.maihe.elg.operation.service.order.GuaranteeAttachmentInfoService;
import cn.maihe.elg.operation.service.system.UserService;
import cn.maihe.elg.operation.supports.system.ElgResourceService;
import cn.maihe.elg.operation.utils.AESFileUtil;
import cn.maihe.elg.operation.utils.DateUtil;
import cn.maihe.elg.operation.utils.NR;
import cn.maihe.elg.operation.utils.OkHttpUtils;
import cn.maihe.elg.operation.utils.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @Description 出函机构出函后通知 电子保函至平台侧
 * 出函机构（保险公司/银行）在出函后向电子保函平台推送电子保函的下载地址
 * @Author WJH
 * @Date 2021/04/21
 */
@Service
@Slf4j
public class XMBondDownloadChannelService extends HyMakeBaseChannelService<BondDownLoadReqDTO, ChlBizRespData> {

    @Resource
    private ElgResourceService elgResourceService;
    @Resource
    private AcceptOrderInfoService acceptOrderInfoService;
    @Resource
    private GuaranteeAttachmentInfoService guaranteeAttachmentInfoService;

    @Resource
    private BidCustomerService bidCustomerService;
    @Resource
    private UserService userService;

    @Resource
    private HyMakeInfoConfig hyMakeInfoConfig;


    @Override
    protected HyMakeChannelMethod methodEnum() {
        return HyMakeChannelMethod.bondDownLoad;
    }

    public InnerBaseResp<String> bondDownload(PushCenterGuaranteeBo bo) {
        HyMakeCenterAuthConfig authConfig = hyMakeInfoConfig.getAuthConfig(CenterNoEnum.XIAMEN);
        GuaranteeVo vo = bo.getGuaranteeVo();
        InnerBaseResp<String> innerBaseResp;
        String url = authConfig.getCenterInfo().getUrl().trim();

        AcceptOrderInfoDO acceptOrderInfo = acceptOrderInfoService.getByAcceptOrderNo(vo.getAcceptOrderNo());
        Assert.notNull(acceptOrderInfo, "查无此受理订单记录");

        HyMakeCenterAuthConfig hyMakeCenterAuthConfig = hyMakeInfoConfig.getAuthConfig(CenterNoEnum.XIAMEN);
        String appId = hyMakeCenterAuthConfig.getCenterInfo().getAppId();
        String applyId = acceptOrderInfo.getCenterApplyId();

        // 3.1.6 推送支付结果
        Map<String, Object> paymentParam = buildPushPayParam(bo, acceptOrderInfo, vo, appId, applyId);
        NR<String> paymentNR = OkHttpUtils.postDataByForm(url + XMChannelMethod.pushPaymentResult.getMethod(), paymentParam);

        innerBaseResp = buildInnerBaseResp(paymentNR, "3.1.6、推送支付结果");
        if (!innerBaseResp.getRespCode().getRespCode().equals(InnerRespCode.SUCCESS.getRespCode())) {
            return innerBaseResp;
        }


        // 3.1.7、推送发票、到账凭证信息
        Map<String, Object> invoiceParam = buildPushPZParam(acceptOrderInfo, appId, applyId);
        NR<String> invoiceNR = OkHttpUtils.postDataByForm(url + XMChannelMethod.pushDZPZStatus.getMethod(), invoiceParam);

        innerBaseResp = buildInnerBaseResp(invoiceNR, "3.1.7、推送发票、到账凭证信息");
        ;
        if (!innerBaseResp.getRespCode().getRespCode().equals(InnerRespCode.SUCCESS.getRespCode())) {
            return innerBaseResp;
        }


        // 3.1.8、推送加密的电子投标保函OFD文件及信息摘要
        Map<String, Object> ofdParam = buildPushOFDParam(acceptOrderInfo, appId, applyId);

        Path tempFile; // 创建临时文件
        try {
            tempFile = Files.createTempFile("电子保函OFD加密文件", ".SOFD");
            Files.write(tempFile, (byte[]) ofdParam.get("SOFDFILE")); // 将字节数组写入临时文件
            ofdParam.remove("SOFDFILE");    // 移除参数，Multipart请求，普通参数与文件是分开的存储
        } catch (IOException e) {
            log.error("创建临时文件异常:{} ", e.getMessage());
            throw new RuntimeException(e);
        }

        Map<String, File> files = new HashMap<>();
        files.put("SOFDFILE", tempFile.toFile());

        NR<String> ofdNR = OkHttpUtils.httpPostMultipart(url + XMChannelMethod.pushOFDFile.getMethod(), ofdParam, files);
        innerBaseResp = buildInnerBaseResp(ofdNR, "3.1.8、推送加密的电子投标保函OFD文件及信息摘要");
        if (!innerBaseResp.getRespCode().getRespCode().equals(InnerRespCode.SUCCESS.getRespCode())) {
            return innerBaseResp;
        }

        ChlBizRespData bizRespData = new ChlBizRespData();
        bizRespData.setCode("0");
        bizRespData.setMessage("推送成功");
        try {
            Files.delete(tempFile);
        } catch (IOException e) {
            log.error("删除临时文件失败: {}", e.getMessage());
        }
        log.error("推送成功:{}, 接口：{}", CenterNoEnum.XIAMEN, "3.1.5、3.1.7、3.1.8");
        return InnerBaseResp.success(bizRespData, "推送成功");
    }

    /**
     * 厦门，生成3.1.6 接口请求参数
     *
     * @param bo
     * @param acceptOrderInfo
     * @param vo
     * @param appId
     * @param applyId
     * @return
     */
    private Map<String, Object> buildPushPayParam(PushCenterGuaranteeBo bo, AcceptOrderInfoDO acceptOrderInfo, GuaranteeVo vo, String appId, String applyId) {

        Map<String, Object> reqBodyMap = new HashMap();
        HyMakeCenterAuthConfig hyMakeCenterAuthConfig = hyMakeInfoConfig.getAuthConfig(CenterNoEnum.XIAMEN);

        reqBodyMap.put("APPID", appId);
        reqBodyMap.put("APPLY_ID", applyId);
        //1:表示已支付
        reqBodyMap.put("PAY_STATUS", StringUtil.isNotBlank(acceptOrderInfo.getPayTime()) ? "1" : "2");
        reqBodyMap.put("PAY_TIME", acceptOrderInfo.getPayTime());
        reqBodyMap.put("POLICY_NO", acceptOrderInfo.getGuaranteeNo());


        reqBodyMap.put("TIMESTAMP", DateUtil.getCurrentDate(DateUtil.FORMAT_DADETIME));

        String signature = hyMakeCenterAuthConfig.getHyMakeAuthorization().signature(reqBodyMap);

        reqBodyMap.put("SIGNATURE", signature);

        return reqBodyMap;
    }

    /**
     * 3.1.7 推送发票、到账凭证信息
     *
     * @param acceptOrderInfo
     * @param appId
     * @param applyId
     * @return
     */
    private Map<String, Object> buildPushPZParam(AcceptOrderInfoDO acceptOrderInfo, String appId, String applyId) {

        Map<String, Object> reqBodyMap = new HashMap();
        HyMakeCenterAuthConfig hyMakeCenterAuthConfig = hyMakeInfoConfig.getAuthConfig(CenterNoEnum.XIAMEN);

        reqBodyMap.put("APPID", appId);
        reqBodyMap.put("APPLY_ID", applyId);

        // 发票状态（1：表示已开具）
        reqBodyMap.put("INVOICE_STATUS", "1");

        // 电子发票下载地址，专票可为空
        String invoiceUrl = guaranteeAttachmentInfoService.buildAttachDownloadUrl(acceptOrderInfo.getAcceptOrderNo(), AttachmentFileType.TYPE_8);
        reqBodyMap.put("INVOICE_URL", invoiceUrl);

        // 到账凭证下载地址
        String voucherUrl = guaranteeAttachmentInfoService.buildAttachDownloadUrl(acceptOrderInfo.getAcceptOrderNo(), AttachmentFileType.TYPE_6);
        reqBodyMap.put("VOUCHER_URL", voucherUrl);


        reqBodyMap.put("TIMESTAMP", DateUtil.getCurrentDate(DateUtil.FORMAT_DADETIME));

        String signature = hyMakeCenterAuthConfig.getHyMakeAuthorization().signature(reqBodyMap);
        reqBodyMap.put("SIGNATURE", signature);

        return reqBodyMap;
    }


    /**
     * 3.1.8、推送加密的电子投标保函OFD文件及信息摘要
     *
     * @param acceptOrderInfo
     * @param appId
     * @param applyId
     * @return
     */
    private Map<String, Object> buildPushOFDParam(AcceptOrderInfoDO acceptOrderInfo, String appId, String applyId) {

        Map<String, Object> reqBodyMap = new HashMap();
        HyMakeCenterAuthConfig hyMakeCenterAuthConfig = hyMakeInfoConfig.getAuthConfig(CenterNoEnum.XIAMEN);

        reqBodyMap.put("APPID", appId);
        reqBodyMap.put("APPLY_ID", applyId);

        // 电子投标保函编号
        reqBodyMap.put("GUARANTEE_NO", acceptOrderInfo.getGuaranteeNo());


        GuaranteeAttachmentInfoDO attachmentInfoDO = guaranteeAttachmentInfoService.getTypeAttachmentByAcceptOrderNo(acceptOrderInfo.getAcceptOrderNo(), AttachmentFileType.TYPE_0);
        byte[] ofdSecret = encryptOFD(applyId, attachmentInfoDO, acceptOrderInfo);
        // 加密后的OFD，转换为MD5
        String odfSecretMD5 = DigestUtils.md5DigestAsHex(ofdSecret).toUpperCase();

        // OFD文件的MD5
        reqBodyMap.put("OFDMD5", attachmentInfoDO.getFileMd5());

        // 加密OFD文件的MD5
        reqBodyMap.put("SOFDMD5", odfSecretMD5);

        // 该电子投标保函的状态（1：有效）
        reqBodyMap.put("STATUS", "1");

        reqBodyMap.put("TIMESTAMP", DateUtil.getCurrentDate(DateUtil.FORMAT_DADETIME));

        String signature = hyMakeCenterAuthConfig.getHyMakeAuthorization().signature(reqBodyMap);

        // 不参与签名 使用申请传输的密钥串生成特定的AES密钥, 在通过AES加密后的电子投标保函加密文件, 文件后缀SOFD，电子保函OFD文件按4.2生成，
        reqBodyMap.put("SOFDFILE", ofdSecret);
        reqBodyMap.put("SIGNATURE", signature);

        return reqBodyMap;
    }

    /**
     * 加密ofd文件
     *
     * @param applyId
     * @param attachmentInfoDO
     * @param acceptOrderInfo
     * @return
     */
    private byte[] encryptOFD(String applyId, GuaranteeAttachmentInfoDO attachmentInfoDO, AcceptOrderInfoDO acceptOrderInfo) {
        JSONObject jsonObject = new JSONObject();

        File file = elgResourceService.getFileByRelativePath(attachmentInfoDO.getFilePath());
        String uuid = UUID.randomUUID().toString();


        // 加密部分：C段， OFD 文件，使用AES加密
        byte[] cFinalByte = AESFileUtil.encryptFileToByteArray(file.getAbsolutePath(), uuid);
        String odfSecretMD5 = DigestUtils.md5DigestAsHex(cFinalByte).toUpperCase();

        // 加密部分：B段
        // 时间戳
        jsonObject.put("TIMESTAMP", DateUtil.getCurrentDate(DateUtil.FORMAT_DADETIME));
        jsonObject.put("VERSION", "1.0");
        // 保函申请ID
        jsonObject.put("APPLY_ID", applyId);
        // 文件加密密钥（自行生成，建议UUID）
        jsonObject.put("PWD", uuid);
        // 加密前文件名
        jsonObject.put("SOURCE_FILE_NAME", attachmentInfoDO.getFileName());
        // 加密前文件MD5
        jsonObject.put("SOURCE_FILE_MD5", attachmentInfoDO.getFileMd5());
        // 加密后文件MD5
        jsonObject.put("C_ENCRYPT_FILE_MD5", odfSecretMD5);

        // 使用申请时发送的SECRET_KEY进行AES加密
        byte[] jsonBytes = AESFileUtil.encryptStringToByteArray(jsonObject.toString(), acceptOrderInfo.getUploadKey());
        // 转为16进制
        String json2Hex16 = byteArrayToUpperHex(jsonBytes);
        // 16进制字符串转字节数组
        byte[] bFinalByte = hexToByteArray(json2Hex16);

        // 加密部分：A段; 固定12字节， 格式30+B段字节长度（不够位数前面补0）例子：300000000003 300000000123 300000020123
        String aString = String.format("30%010d", bFinalByte.length);
        byte[] aFinalByte = aString.getBytes(StandardCharsets.UTF_8);

        // 加密文件分为3段，A段+B段+C段
        return concatenateByteArrays(aFinalByte, bFinalByte, cFinalByte);
    }

    private byte[] concatenateByteArrays(byte[] a, byte[] b, byte[] c) {
        // 计算最终字节数组的长度
        int totalLength = a.length + b.length + c.length;

        // 创建最终的字节数组
        byte[] result = new byte[totalLength];

        // 复制A段
        System.arraycopy(a, 0, result, 0, a.length);

        // 复制B段
        System.arraycopy(b, 0, result, a.length, b.length);

        // 复制C段
        System.arraycopy(c, 0, result, a.length + b.length, c.length);

        return result;
    }

    // 字节数组转换为16进制字符串
    public String byteArrayToUpperHex(byte[] bytes) {
        return Hex.encodeHexString(bytes).toUpperCase();
    }

    // 16进制字符串转换为字节数组
    private byte[] hexToByteArray(String hexString) {
        return hexString.getBytes(Charset.defaultCharset());
    }

    // 字节数组转换为16进制字符串
    public String byteArrayToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    // 16进制字符串转换为字节数组
    private byte[] hexStringToByteArray(String hexString) {
        int len = hexString.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
                    + Character.digit(hexString.charAt(i + 1), 16));
        }
        return data;
    }


    private InnerBaseResp buildInnerBaseResp(NR<String> stringNR, String apiString) {
        if (stringNR.isFail()) {
            // 需要测试时候才能确定返回结果是否为JSON格式
            log.error("通知异常:{}", stringNR.getErrorMessage());
            return InnerBaseResp.error("通知异常:" + stringNR.getErrorMessage());
        }

        JSONObject jsonObject = JSONObject.parseObject(stringNR.getData());
        if (!Boolean.TRUE.equals(jsonObject.get("success"))) {
            log.error("通知异常:{} ErrorCode:{}", jsonObject.get("errorDesc"), jsonObject.get("errorCode"));
            return InnerBaseResp.error("通知异常:" + jsonObject.get("errorDesc") + " ErrorCode: " + jsonObject.get("errorCode"));
        }

        log.error("通知成功:{}, 接口：{}", CenterNoEnum.XIAMEN, apiString);
        return InnerBaseResp.success(null, "");
    }

    /**
     * 获取该定的支付地址：无需授权地址
     *
     * @param acceptOrderInfo
     * @return
     */
    private String buildPayUrl(AcceptOrderInfoDO acceptOrderInfo) {
        Map<String, Object> params = new HashMap<>();
        params.put("bidCustomerId", acceptOrderInfo.getBidCustomerId());
        params.put("acceptOrderNo", acceptOrderInfo.getAcceptOrderNo());
        String paramsJson = JSON.toJSONString(params);

        String paramsB64 = Base64.encode(paramsJson);

        String payUrl = elgResourceService.buildElgFrontUrl("/#/payUrl/" + paramsB64);
        return payUrl;
    }
}
